package com.xjd.edu.toolkit.helper;

import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;

/**
 * App 版本号工具类
 *
 * @author wangyu
 * @since 2023/3/20
 */
public class AppVersionHelper {

    private static final String APP_VERSION_REGEX = "\\d+(\\.\\d+){2,}";

    private AppVersionHelper() {
    }

    /**
     * 快捷比较方法 ($source == $target)，详情参考 {@link #cmp(String, String)}
     *
     * @param sourceVersion App源版本号，非空
     * @param targetVersion App目标版本号，非空
     * @return
     * @see #cmp(String, String)
     */
    public static boolean eq(String sourceVersion, String targetVersion) {
        return cmp(sourceVersion, targetVersion) == 0;
    }

    /**
     * 快捷比较方法 ($source > $target)，详情参考 {@link #cmp(String, String)}
     *
     * @param sourceVersion App源版本号，非空
     * @param targetVersion App目标版本号，非空
     * @return
     * @see #cmp(String, String)
     */
    public static boolean gt(String sourceVersion, String targetVersion) {
        return cmp(sourceVersion, targetVersion) > 0;
    }

    /**
     * 快捷比较方法 ($source >= $target)，详情参考 {@link #cmp(String, String)}
     *
     * @param sourceVersion App源版本号，非空
     * @param targetVersion App目标版本号，非空
     * @return
     * @see #cmp(String, String)
     */
    public static boolean gte(String sourceVersion, String targetVersion) {
        return cmp(sourceVersion, targetVersion) >= 0;
    }

    /**
     * 快捷比较方法 ($source < $target)，详情参考 {@link #cmp(String, String)}
     *
     * @param sourceVersion App源版本号，非空
     * @param targetVersion App目标版本号，非空
     * @return
     * @see #cmp(String, String)
     */
    public static boolean lt(String sourceVersion, String targetVersion) {
        return cmp(sourceVersion, targetVersion) < 0;
    }

    /**
     * 快捷比较方法 ($source <= $target)，详情参考 {@link #cmp(String, String)}
     *
     * @param sourceVersion App源版本号，非空
     * @param targetVersion App目标版本号，非空
     * @return
     * @see #cmp(String, String)
     */
    public static boolean lte(String sourceVersion, String targetVersion) {
        return cmp(sourceVersion, targetVersion) <= 0;
    }

    /**
     * 比较两个App版本号大小，仅支持带 . 格式版本号，且至少有三部分，如：3.2.18，每部分都必须是整数
     *
     * @param sourceVersion App源版本号，非空
     * @param targetVersion App目标版本号，非空
     * @return 0 表示相等、1表示 source > target、-1表示 source < target
     */
    public static int cmp(String sourceVersion, String targetVersion) {

        Assert.notNull(sourceVersion, "源App版本号不能为空");
        Assert.notNull(targetVersion, "目标App版本号不能为空");

        // 兼容测试版本号后面的 -dev | -test 等环境后缀
        sourceVersion = trimVersionSuffix(sourceVersion);
        targetVersion = trimVersionSuffix(targetVersion);

        // 兼容纯数字情形，目前仅存在3位整数的版本号，如：264 => 2.6.4，这里将其转换为标准格式
        sourceVersion = transformVersion(sourceVersion);
        targetVersion = transformVersion(targetVersion);

        // 校验版本号格式
        Assert.isTrue(isLegalAppVersion(sourceVersion), "源App版本号不合法");
        Assert.isTrue(isLegalAppVersion(targetVersion), "目标App版本号不合法");

        // 相等情况，快速比较
        if (sourceVersion.equals(targetVersion)) {
            return 0;
        }

        // 拆解每一部分，从高位开始比较
        final List<Integer> sourceItems = appVersionParts(sourceVersion);
        final List<Integer> targetItems = appVersionParts(targetVersion);

        if (sourceItems.size() > targetItems.size()) {
            fillZero(targetItems, sourceItems.size() - targetItems.size());
        } else if (sourceItems.size() < targetItems.size()) {
            fillZero(sourceItems, targetItems.size() - sourceItems.size());
        }

        final int size = Math.min(sourceItems.size(), targetItems.size());
        for (int i = 0; i < size; i++) {
            if (sourceItems.get(i) > targetItems.get(i)) {
                return 1;
            }
            if (sourceItems.get(i) < targetItems.get(i)) {
                return -1;
            }
        }

        return 0;   // 通常不会执行到这一行，只有当补0后相等时才会走到这一行
    }

    /**
     * 判断 App 版本号是否合法
     *
     * @param appVersion App版本号，非空
     * @return 是否合法
     */
    static boolean isLegalAppVersion(String appVersion) {
        return appVersion != null && appVersion.matches(APP_VERSION_REGEX);
    }

    /**
     * 去除版本号后缀，如：3.2.18-dev => 3.2.18
     *
     * @param version 非空版本号
     * @return
     */
    private static String trimVersionSuffix(String version) {
        if (version.contains("-")) {
            version = version.trim().split("\\-")[0];
        }

        return version;
    }

    /**
     * 兼容纯数字情形，目前仅存在3位或4位整数的版本号（3位需要在最后1位前补0，使之成为4位），如：264 => 2604 => 2.6.4，这里将其转换为标准格式
     *
     * @param version 非空版本号
     * @return
     */
    private static String transformVersion(String version) {
        if (version.matches("\\d{3,4}")) {
            final char one = version.charAt(0);
            final char two = version.charAt(1);
            if (version.length() == 3) {
                version = String.format("%s.%s.%s", one, two, version.charAt(2));
            } else {
                version = String.format("%s.%s.%s", one, two, Integer.parseInt(version.substring(2)));
            }
        }

        return version;
    }

    private static List<Integer> appVersionParts(String appVersion) {
        final List<Integer> items = new ArrayList<>();
        final String[] parts = appVersion.split("\\.");
        for (String part : parts) {
            items.add(Integer.valueOf(part));
        }
        return items;
    }

    /**
     * 补0操作
     *
     * @param items 整数列表
     * @param count 补0数量
     */
    private static void fillZero(List<Integer> items, int count) {
        for (int i = 0; i < count; i++) {
            items.add(0);
        }
    }

}
